#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

const int N = 2e5 + 5;

struct node
{
    pair<int, int> p;
    int val;
    bool operator<(const node &other) const
    {
        if (val != other.val)
            return val < other.val;
        else
            return p < other.p;
    }
};

// priority_queue<node, vector<node>, greater<node>> pq;

pair<int, int> edges[N][3];
struct P3
{
    int x, y, z;
    P3(int x = 0, int y = 0, int z = 0) : x(x), y(y), z(z) {}
} a[N];

int n;

pair<int, int> ordered(int x, int y)
{
    if (x > y)
        swap(x, y);
    return make_pair(x, y);
}

// unordered_map<pair<int, int>, int> disc;
// gp_hash_table<pair<int, int>, int> disc;
map<pair<int, int>, int> disc;

set<int> buckets[N * 3];
pair<int, int> rd[N];
int bcnt;

inline int checkin(const pair<int, int> &p)
{
    // static int cnt = 0;
    if (disc.find(p) != disc.end())
        return disc[p];
    else
    {
        rd[bcnt] = p;
        return disc[p] = bcnt++;
    }
}

vector<int> G[N];
bool vis[N];
void dfs(int u, vector<int> &ans)
{
    // static bool vis[N];
    vis[u] = 1;
    ans.push_back(u);
    for (int v : G[u])
        if (!vis[v])
            dfs(v, ans);
}

inline bool valid(int triId)
{
    int id[3];
    for (int j = 0; j < 3; ++j)
    {
        id[j] = buckets[checkin(edges[triId][j])].size();
    }
    sort(id, id + 3);
    return id[0] == 1 && id[1] == 1;
}

int eid[N][3];

int main(int argc, char const *argv[])
{
    // freopen("in.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    for (int kk = 0; kk < t; ++kk)
    {
        disc.clear();
        for (int i = 0; i <= n; ++i)
            G[i].clear();
        bcnt = 0;

        scanf("%d", &n);
        for (int i = 0; i < n - 2; ++i)
        {
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
            edges[i][0] = ordered(a[i].x, a[i].y);
            edges[i][1] = ordered(a[i].y, a[i].z);
            edges[i][2] = ordered(a[i].x, a[i].z);

            for (int j = 0; j < 3; ++j)
            {
                eid[i][j] = checkin(edges[i][j]);
                buckets[eid[i][j]].insert(i);
            }
        }

        for (int i = 0; i < bcnt; ++i)
        {
            if (buckets[i].size() == 1)
            {
                vis[i] = true;
                // in[i] = true;
                const auto &p = rd[i];
                G[p.first].push_back(p.second);
                G[p.second].push_back(p.first);
            }
        }
        vector<int> ans;
        memset(vis + 1, 0, sizeof(bool) * n);
        dfs(1, ans);

        for (int i = 0; i < n; ++i)
        {
            assert(ans[i] >= 1 && ans[i] <= n);
            printf("%d%c", ans[i], i == n - 1 ? '\n' : ' ');
        }

        queue<int> Q;
        vector<int> ord;
        static bool in[N];
        memset(in, 0, sizeof(bool) * (n + 1));

        for (int i = 0; i < n-2; ++i)
        {
            if (valid(i))
            {
                Q.push(i);
                in[i] = true;
            }
        }

        while (!Q.empty())
        {
            int nowTid = Q.front();
            Q.pop();
            ord.push_back(nowTid);
            for (int j = 0; j < 3; ++j)
                buckets[eid[nowTid][j]].erase(nowTid);
            for (int j = 0; j < 3; ++j)
            {
                for (int newTid : buckets[eid[nowTid][j]])
                    if (!in[newTid] && valid(newTid))
                    {
                        in[newTid] = true;
                        Q.push(newTid);
                    }
            }
        }

        for (int i = 0; i < n - 2; ++i)
        {
            assert(ord[i] >= 0 && ord[i] < n - 2);
            printf("%d%c", ord[i] + 1, i == n - 3 ? '\n' : ' ');
        }
        for (int i=0; i<bcnt; ++i) buckets[i].clear();
    }

    return 0;
}
